/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

import java.beans.Introspector;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * 核心功能 (Core Function):
 * - 根据注解（如 @Component, @Repository, @Service, @Controller, @Named）提取 Bean 名称。
 * - 如果注解没有指定 value 属性，则根据类名生成默认 beanName。
 * <p>
 * 主要逻辑流程:
 * 1. 检查是否为 {@link AnnotatedBeanDefinition}（支持注解元数据）。
 * 2. 尝试从注解（@Component 或派生注解）获取 beanName。
 * 3. 如果注解中没有指定 name，则回退到默认命名策略（类短名首字母小写）。
 * <p>
 * 在 Spring 框架中的作用:
 * - 在组件扫描（ClassPathBeanDefinitionScanner）和配置类解析时，作为 BeanNameGenerator 的默认实现被调用。
 * - 前置调用：在扫描包时生成 BeanDefinition 并调用 generateBeanName。
 * - 后续调用：生成的 beanName 会被注册到 BeanDefinitionRegistry 中。
 * <p>
 * {@link BeanNameGenerator} implementation for bean classes annotated with the
 * {@link org.springframework.stereotype.Component @Component} annotation or
 * with another annotation that is itself annotated with {@code @Component} as a
 * meta-annotation. For example, Spring's stereotype annotations (such as
 * {@link org.springframework.stereotype.Repository @Repository}) are
 * themselves annotated with {@code @Component}.
 *
 * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
 * JSR-330's {@link javax.inject.Named} annotations, if available. Note that
 * Spring component annotations always override such standard annotations.
 *
 * <p>If the annotation's value doesn't indicate a bean name, an appropriate
 * name will be built based on the short name of the class (with the first
 * letter lower-cased). For example:
 *
 * <pre class="code">com.xyz.FooServiceImpl -&gt; fooServiceImpl</pre>
 *
 * @author Juergen Hoeller
 * @author Mark Fisher
 * @see org.springframework.stereotype.Component#value()
 * @see org.springframework.stereotype.Repository#value()
 * @see org.springframework.stereotype.Service#value()
 * @see org.springframework.stereotype.Controller#value()
 * @see javax.inject.Named#value()
 * @see FullyQualifiedAnnotationBeanNameGenerator
 * @since 2.5
 */
public class AnnotationBeanNameGenerator implements BeanNameGenerator {

	/**
	 * A convenient constant for a default {@code AnnotationBeanNameGenerator} instance,
	 * as used for component scanning purposes.
	 * <p>
	 * 一个默认的 {@code AnnotationBeanNameGenerator} 实例常量，主要用于组件扫描。
	 */
	public static final AnnotationBeanNameGenerator INSTANCE = new AnnotationBeanNameGenerator();

	/**
	 * 使用字符串常量保存 Spring 核心 stereotype 注解 @Component 的全限定类名。
	 * 1. 用于判断某个注解是否是 @Component 本身，或者是否带有 @Component 元注解（例如 @Service、@Repository、@Controller）。
	 * 2. 通过字符串比较而不是类引用来判断，避免类加载问题，降低编译时依赖。
	 * 3. 是 Spring stereotype（组件标识）检测的基础依据。
	 */
	private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";

	// 缓存注解的元注解类型，加速 stereotype 检查
	private final Map<String, Set<String>> metaAnnotationTypesCache = new ConcurrentHashMap<>();


	@Override
	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		if (definition instanceof AnnotatedBeanDefinition) {
			// 如果是带注解的 BeanDefinition，尝试从注解中解析 beanName
			String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
			if (StringUtils.hasText(beanName)) {
				// Explicit bean name found.
				// 找到显式的 beanName，直接返回
				return beanName;
			}
		}
		// Fallback: generate a unique default bean name.
		// 回退方案：生成基于类名的默认 beanName
		return buildDefaultBeanName(definition, registry);
	}

	/**
	 * Derive a bean name from one of the annotations on the class.
	 * 从类上的注解中推导 beanName。
	 *
	 * @param annotatedDef the annotation-aware bean definition
	 *                     包含注解元数据的 BeanDefinition
	 * @return the bean name, or {@code null} if none is found
	 * 返回 beanName，如果没有找到则返回 null
	 */
	@Nullable
	protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
		AnnotationMetadata amd = annotatedDef.getMetadata();
		Set<String> types = amd.getAnnotationTypes();
		String beanName = null;
		for (String type : types) {
			AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
			if (attributes != null) {
				// 读取元注解类型，缓存结果避免重复解析
				Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
					Set<String> result = amd.getMetaAnnotationTypes(key);
					return (result.isEmpty() ? Collections.emptySet() : result);
				});
				// 判断是否是 stereotype 类型的注解，且支持 value 属性作为 beanName
				if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
					Object value = attributes.get("value");
					if (value instanceof String) {
						String strVal = (String) value;
						if (StringUtils.hasLength(strVal)) {
							// 如果多个 stereotype 注解提供了不同的名称，抛出异常
							if (beanName != null && !strVal.equals(beanName)) {
								throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
										"component names: '" + beanName + "' versus '" + strVal + "'");
							}
							beanName = strVal;
						}
					}
				}
			}
		}
		return beanName;
	}

	/**
	 * Check whether the given annotation is a stereotype that is allowed
	 * to suggest a component name through its annotation {@code value()}.
	 * <p>
	 * 检查给定的注解是否为 stereotype 注解，且允许通过其 value 属性指定组件名。
	 *
	 * @param annotationType      the name of the annotation class to check
	 *                            要检查的注解类名
	 * @param metaAnnotationTypes the names of meta-annotations on the given annotation
	 *                            该注解上的元注解类型
	 * @param attributes          the map of attributes for the given annotation
	 *                            注解的属性集合
	 * @return whether the annotation qualifies as a stereotype with component name
	 * 是否符合 stereotype 条件并能提供组件名称
	 */
	protected boolean isStereotypeWithNameValue(String annotationType,
												Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) {

		// stereotype 注解条件：
		// 1. 注解本身是 @Component
		// 2. 注解带有 @Component 元注解（如 @Repository, @Service）
		// 3. 标准 Java EE 注解：@ManagedBean 或 @Named
		boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
				metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
				annotationType.equals("javax.annotation.ManagedBean") ||
				annotationType.equals("javax.inject.Named");

		// 需要注解有 value 属性，才能作为 beanName 来源
		return (isStereotype && attributes != null && attributes.containsKey("value"));
	}

	/**
	 * Derive a default bean name from the given bean definition.
	 * <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}.
	 * <p>
	 * 从给定的 BeanDefinition 推导默认的 beanName。
	 * 默认实现委托给 {@link #buildDefaultBeanName(BeanDefinition)}。
	 *
	 * @param definition the bean definition to build a bean name for
	 *                   目标 BeanDefinition
	 * @param registry   the registry that the given bean definition is being registered with
	 *                   BeanDefinition 将要注册的容器
	 * @return the default bean name (never {@code null})
	 * 默认的 beanName（不会为 null）
	 */
	protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		return buildDefaultBeanName(definition);
	}

	/**
	 * Derive a default bean name from the given bean definition.
	 * <p>The default implementation simply builds a decapitalized version
	 * of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao".
	 * <p>Note that inner classes will thus have names of the form
	 * "outerClassName.InnerClassName", which because of the period in the
	 * name may be an issue if you are autowiring by name.
	 * <p>
	 * 基于类名生成默认的 beanName。
	 * 默认策略：取短类名，并将首字母小写。
	 * 示例："mypackage.MyJdbcDao" -> "myJdbcDao"
	 * 注意：内部类的名称中可能包含 '.'，在按名称自动装配时可能造成问题。
	 *
	 * @param definition the bean definition to build a bean name for
	 *                   目标 BeanDefinition
	 * @return the default bean name (never {@code null})
	 * 默认 beanName（不会为 null）
	 */
	protected String buildDefaultBeanName(BeanDefinition definition) {
		String beanClassName = definition.getBeanClassName();
		Assert.state(beanClassName != null, "No bean class name set");
		String shortClassName = ClassUtils.getShortName(beanClassName);
		// decapitalize = 首字母小写
		return Introspector.decapitalize(shortClassName);
	}

}
